#include "./polygondata.h"
#include "./polygondataiterators.h"
#include <QSqlError>
#include <QDebug>
#include <QSqlQuery>
#include <QProgressDialog>

QSharedPointer<PolygonData> PolygonData::_instance;

PolygonData::PolygonData()
{
    _maxId = 0;
}

QSharedPointer<PolygonData> PolygonData::instance()
{
    if(_instance.isNull())
    {
        _instance = QSharedPointer<PolygonData>(new PolygonData);
    }
    return _instance;
}

void PolygonData::_setValueEmf(int id, double value)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        item->valueEmf = value;
    }
}

bool PolygonData::load(QString idPolygon)
{
    _setMeasurementType(idPolygon);
    if(measurementType.compare(COAXIAL) != 0)
    {
        _loadLoops(idPolygon);
    }
    _loadPickets(idPolygon);
    return true;
}

void PolygonData::_loadLoops(QString idPolygon)
{
    DbFabric fabric;
    QSharedPointer<QSqlRelationalTableModelWithReconnection> modelLoop = fabric.createGeneratorLoopModel();
    QString filter = "GeneratorLoop.idPolygon=" + idPolygon;
    modelLoop->setFilter(filter);
    modelLoop->select();

    QSharedPointer<LoopDrawableBuilder> loopDrawableBuilder(new LoopDrawableBuilder());
    QSharedPointer<LoopInfoDockBuilder> loopInfoDockBuilder(new LoopInfoDockBuilder());
    QSharedPointer<StandartItemBuilder> loopStandartItemBuilder(new StandartItemBuilder());

    for(int i=0; i<modelLoop->rowCount(); i++)
    {
        QSqlRecord record = modelLoop->record(i);
        PolygonItem *item = new PolygonItem();
        item->sqlFilter = "idGeneratorLoop=" + record.value("idGeneratorLoop").toString();
        item->model = modelLoop;
        item->drawableBuilder = loopDrawableBuilder;
        item->infodockBuilder = loopInfoDockBuilder;
        item->standardItemBuilder = loopStandartItemBuilder;
        item->dbRecord.push_back(record);
        _loops.insert(_maxId, item);
        _maxId++;
    }
}

void PolygonData::_loadPickets(QString idPolygon)
{
    DbFabric fabric;
    QSharedPointer<QSqlRelationalTableModelWithReconnection> modelPicket = fabric.createPicketModel();
    QString filter = QString("relTblAl_%1.idPolygon=" + idPolygon).arg(DbFabric::PICKET_MODEL_ID_LOOP);
    modelPicket->setFilter(filter);
    modelPicket->select();

    QString error;
    error = modelPicket->lastError().text();

    QSharedPointer<PicketDrawableBuilder> picketDrawableBuilder(new PicketDrawableBuilder());
    QSharedPointer<PicketInfoDockBuilder> picketInfoDockBuilder(new PicketInfoDockBuilder());
    QSharedPointer<StandartItemBuilder> picketStandartItemBuilder(new StandartItemBuilder());

    for(int i=0; i<modelPicket->rowCount(); i++)
    {
        QSqlRecord record = modelPicket->record(i);
        PolygonItem *item = new PolygonItem();
        item->sqlFilter = "idPicket=" + record.value("idPicket").toString();
        item->model = modelPicket;
        item->drawableBuilder = picketDrawableBuilder;
        item->infodockBuilder = picketInfoDockBuilder;
        item->standardItemBuilder = picketStandartItemBuilder;
        item->dbRecord.push_back(record);

        _pickets.insert(_maxId, item);
        _maxId++;
    }
}

QSharedPointer<ElementIterator> PolygonData::createLoopIterator()
{
    return QSharedPointer<ElementIterator>(new LoopIterator);
}

QSharedPointer<ElementIterator> PolygonData::createPicketIterator()
{
    return QSharedPointer<ElementIterator>(new PicketIterator);
}

int PolygonData::getPicketsCount()
{
    return _pickets.size();
}

QSharedPointer<ElementIterator> PolygonData::createEmfIterator()
{
    return QSharedPointer<ElementIterator>(new EmfIterator);
}

QVector<QStandardItem *> PolygonData::getStandardItem(int id)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        if(!item->dbRecord.isEmpty())
            item->standardItemBuilder->setData(id, item->dbRecord);
        else
            item->standardItemBuilder->setData(id, item->sqlFilter, item->model);
        return item->standardItemBuilder->build();
    }
    return QVector<QStandardItem*>();
}

QLayout * PolygonData::getInfoDock(int id, void *data)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        if(!item->dbRecord.isEmpty())
            item->infodockBuilder->setData(id, item->dbRecord);
        else
            item->infodockBuilder->setData(id, item->sqlFilter, item->model);
        return item->infodockBuilder->build(data);
    }
    return NULL;
}

QString PolygonData::getSqlFilter(int id)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        return item->sqlFilter;
    }
    return NULL;
}

QVector<QSqlRecord> PolygonData::getSqlRecord(int id)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        return item->dbRecord;
    }
    return QVector<QSqlRecord>();
}

double PolygonData::getValueEmf(int id)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        return item->valueEmf;
    }
    return NULL;
}

QMap<QString, Drawable *> PolygonData::getDrawable(int id)
{
    PolygonItem *item = _findPolygonItemById(id);
    if(item != NULL)
    {
        if(!item->dbRecord.isEmpty())
            item->drawableBuilder->setData(id, item->dbRecord);
        else
            item->drawableBuilder->setData(id, item->sqlFilter, item->model);

        return item->drawableBuilder->build();
    }
    return QMap<QString, Drawable *>();
}

PolygonItem* PolygonData::_findPolygonItemById(int id)
{
    if(_loops.contains(id))
    {
        return _loops.value(id);
    }
    else if(_pickets.contains(id))
    {
        return _pickets.value(id);
    }
    else if(_emfs.contains(id))
    {
        return _emfs.value(id);
    }
    return NULL;
}

bool PolygonData::loadEmf(int id)
{
    if(_pickets.contains(id))
    {
        _loadEmfByPicket(id);
    }
    else if(_loops.contains(id))
    {
        _loadEmfByLoop(id);
    }
    return false;
}

bool PolygonData::loadEmfByTime(int id, QString time)
{
    QSqlRecord picket = _pickets.value(id)->dbRecord[0];
    int idEmf = picket.value("idEmf").toInt();
    QString filter = "idEmf='" + QString::number(idEmf) + "'";

    QString queryText = "SELECT 0.5*(v1 + v2) FROM ( SELECT value as v1 FROM Emf WHERE "
                        + filter +
                        " AND Emf.time <= :time1 ORDER BY Emf.time DESC LIMIT 1) t1 JOIN (SELECT value v2 FROM Emf WHERE "
                        + filter +
                        " AND Emf.time >= :time2 ORDER BY Emf.time ASC LIMIT 1) t2";


    QSqlQuery query;
    query.prepare(queryText);
    query.bindValue(":time1", time);
    query.bindValue(":time2", time);
    if(query.exec())
    {
        query.first();
        _setValueEmf(id, query.value(0).toDouble());
    }
    else
       _setValueEmf(id, INVALID_EMF_VALUE);

    return true;
}

bool PolygonData::loadAverageEmf(QProgressDialog *progressHandler)
{
    _emfs.clear();
    //QString queryText = "SELECT time, AVG(value) as value FROM Emf INNER JOIN Picket ON Emf.idEmf = Picket.idEmf INNER JOIN GeneratorLoop ON GeneratorLoop.idGeneratorLoop = Picket.idGeneratorLoop AND GeneratorLoop.idPolygon = " + idPolygon + " GROUP BY time";
    QSharedPointer<ElementIterator> iterator = createPicketIterator();
    QString where = "WHERE Emf.idEmf IN (";
    int picketCount = 10;
    for(iterator->begin();!iterator->isFinished();iterator->next())
    {
        int id = iterator->currentKey();
        QSqlRecord picket = _pickets[id]->dbRecord[0];
        if(picket.count() > 0)
        {
            where.append(picket.value("idEmf").toString());
            where.append(" , ");
            qApp->processEvents();
            if(progressHandler)
            {
                progressHandler->setValue((25*picketCount)/(_pickets.size()));
            }
        }
        picketCount++;
    }
    where.append("-1)");

    QString queryText = "SELECT time, AVG(value) as value FROM Emf " + where +  " GROUP BY time";


    QSqlQuery query;
    query.prepare(queryText);
    QTime t;
    t.start();
    qApp->processEvents();
    if(progressHandler)
    {
        progressHandler->setValue(25);
    }

    if(query.exec())
    {
        PolygonItem *item = new PolygonItem();

        QSharedPointer<EmfDrawableBuilder> emfDrawableBuilder(new EmfDrawableBuilder());
        QSharedPointer<EmfInfoDockBuilder> emfInfoDockBuilder(new EmfInfoDockBuilder());

        item->drawableBuilder = emfDrawableBuilder;
        item->infodockBuilder = emfInfoDockBuilder;

        while(query.next())
        {
            QSqlRecord record =query.record();
            item->dbRecord.push_back(record);
        }
        _emfs.insert(_maxId, item);
        _maxId++;
    }
    qApp->processEvents();
    if(progressHandler)
    {
        progressHandler->setValue(100);
    }

    qDebug() << "Loading average emf: " << t.elapsed();
    return true;
}

void PolygonData::_loadEmfByPicket(int id)
{
    DbFabric fabric;
    QSharedPointer<QSqlRelationalTableModelWithReconnection> modelEmf = fabric.createEmfModel();
    QString filter = "";

    int totalTime = 0;
    QSqlRecord picket = _pickets.value(id)->dbRecord[0];
    if(picket.count() > 0)
    {
        int idEmf = picket.value("idEmf").toInt();
        filter = "idEmf='" + QString::number(idEmf) + "'";
    }
    else
    {
        modelEmf->setRelation(DbFabric::EMF_MODEL_ID_EMF, QSqlRelation("Picket", "idEmf", "idEmf"));
        QString("relTblAl_%1." + _pickets.value(id)->sqlFilter).arg(DbFabric::EMF_MODEL_ID_EMF);
    }

    modelEmf->setFilter(filter);
    QTime timeEmf;
    timeEmf.start();
    modelEmf->select();
    totalTime = totalTime +  timeEmf.elapsed();
    qDebug("Emf time elapsed: %d ms",totalTime);

    QString error;
    error = modelEmf->lastError().text();

    QSharedPointer<EmfDrawableBuilder> emfDrawableBuilder(new EmfDrawableBuilder());
    QSharedPointer<EmfInfoDockBuilder> emfInfoDockBuilder(new EmfInfoDockBuilder());

    QSqlRecord record = modelEmf->record(0);
    PolygonItem *item = new PolygonItem();
    item->sqlFilter = "Emf.idEmf=" + record.value("idEmf").toString();
    item->model = modelEmf;

    item->drawableBuilder = emfDrawableBuilder;
    item->infodockBuilder = emfInfoDockBuilder;

    for(int i=0; i<modelEmf->rowCount(); i++)
    {
        QSqlRecord record = modelEmf->record(i);
        item->dbRecord.push_back(record);
    }


    _emfs.insert(_maxId, item);
    _maxId++;
}

void PolygonData::_loadEmfByLoop(int id)
{
    DbFabric fabric;
    QSharedPointer<QSqlRelationalTableModelWithReconnection> modelEmf = fabric.createEmfModel();
    modelEmf->setRelation(DbFabric::EMF_MODEL_ID_EMF, QSqlRelation("Picket", "idEmf", "idEmf"));

    QString filter = QString("relTblAl_%1." + _loops.value(id)->sqlFilter).arg(DbFabric::EMF_MODEL_ID_EMF);

    modelEmf->setFilter(filter);
    modelEmf->select();

    QString error;
    error = modelEmf->lastError().text();

    QSharedPointer<EmfDrawableBuilder> emfDrawableBuilder(new EmfDrawableBuilder());
    //QSharedPointer<EmfInfoDockBuilder> emfInfoDockBuilder(new EmfInfoDockBuilder());

    for(int i = 0; i < modelEmf->rowCount(); i++)
    {
        QSqlRecord record = modelEmf->record(i);
        PolygonItem *item = new PolygonItem();
        item->sqlFilter = "Emf.idEmf=" + record.value("idEmf").toString();
        item->model = modelEmf;

        item->drawableBuilder = emfDrawableBuilder;

        _emfs.insert(_maxId, item);
        QString str = record.value("idEmf").toString();
        qDebug(str.toUtf8().data());
        _maxId++;
    }

}

void PolygonData::clear()
{
    _pickets.clear();
    _loops.clear();
    _emfs.clear();
}

void PolygonData::_setMeasurementType(QString idPolygon)
{
    DbFabric fabric;
    QSharedPointer <QSqlRelationalTableModelWithReconnection> polygonModel = fabric.createPolygonModel();
    QString filter = "Polygon.idPolygon=" + idPolygon;
    polygonModel->setFilter(filter);
    polygonModel->select();

    QString measureType = polygonModel->record(0).value("type").toString();
    if(measureType.compare(COAXIAL) == 0)
    {
        measurementType = COAXIAL;
    }
    else
    {
        measurementType = USUAL;
    }
}
